package base

import (
	"fmt"
	"gin/common/lib/gorm"
	error2 "gin/util/error"
	"math"

	gorm2 "gorm.io/gorm"
)

const interval = 100000
const maxOrder = 1<<31 - 1

type TableNo struct {
	Id int64
	No int64
}

// Top 置顶
func Top(id int64, tableName, fileName string) {
	err := gorm.GinDb.Transaction(func(tx *gorm2.DB) error {
		if err := tx.Table(tableName).Updates(map[string]interface{}{
			fileName: gorm2.Expr(fileName+"+?", 1),
		}).Error; err != nil {
			return err
		}
		return tx.Model(tableName).Where("id = ?", id).Update(fileName, 1).Error
	})
	if err != nil {
		panic(err.Error())
	}
}

// Move 移动
func Move(id int64, tableName, fileName string, genre byte) {
	var currentNo int64
	var err error

	err = gorm.GinDb.Transaction(func(tx *gorm2.DB) error {
		if err = tx.Table(tableName).
			Where("id = ?", id).
			Pluck(fileName, &currentNo).Error; err != nil {
			return err
		}

		tableNo := TableNo{}
		if genre == 1 {
			// 上移
			tableNo, err = MaxCurrentNo(tx, tableName, fileName, currentNo)
			if err != nil {
				return err
			}
			if tableNo.Id == 0 {
				return nil
			}

		} else {
			// 下移
			tableNo, err = MinCurrentNo(tx, tableName, fileName, currentNo)
			if err != nil {
				return err
			}

			if tableNo.Id == 0 {
				return nil
			}
		}

		if err = tx.Table(tableName).Where("id = ?", tableNo.Id).Update(fileName, currentNo).Error; err != nil {
			return err
		}

		return tx.Table(tableName).Where("id = ?", tableNo.Id).Update(fileName, tableNo.No).Error

	})

	if err != nil {
		panic(err.Error())
	}
}

// MaxCurrentNo 获取当前位置上方的最大值
func MaxCurrentNo(db *gorm2.DB, tableName, fileName string, currentNo int64) (TableNo, error) {
	tableNo := TableNo{}
	sql := fmt.Sprintf("select id,max(%s) no from %s where %s < %d", fileName, tableName, fileName, currentNo)
	if err := db.Raw(sql).Limit(1).Find(&tableNo).Error; err != nil {
		return tableNo, err
	}

	err := db.Table(tableName).Select("id").Where(fileName+" = ? ", tableNo.No).
		Pluck("id", &tableNo.Id).Error
	return tableNo, err
}

// MinCurrentNo 获取当前位置下方的最小值
func MinCurrentNo(db *gorm2.DB, tableName, fileName string, currentNo int64) (TableNo, error) {
	tableNo := TableNo{}
	sql := fmt.Sprintf("select id,min(%s) no from %s where %s > %d", fileName, tableName, fileName, currentNo)
	if err := db.Raw(sql).Limit(1).Find(&tableNo).Error; err != nil {
		return tableNo, err
	}
	err := db.Table(tableName).Select("id").Where(fileName+" = ? ", tableNo.No).
		Pluck("id", &tableNo.Id).Error
	return tableNo, err
}

// GenerateMaxNo 获取最大值
func GenerateMaxNo(tableName, fileName, condtion string) int64 {
	var max int64
	sql := fmt.Sprintf("Select max(%s) max from %s", fileName, tableName)
	if condtion != "" {
		sql += "Where" + condtion
	}
	gorm.GinDb.Raw(sql).Pluck("max", &max)
	max += interval
	return max
}

// GenerateMinNo 获取最小值
func GenerateMinNo(tableName, fileName, condition string) int64 {
	var min int64
	sql := fmt.Sprintf("select min(%s) min from %s", fileName, tableName)
	if condition != "" {
		sql += "Where" + condition
	}
	gorm.GinDb.Raw(sql).Pluck("min", &min)
	min -= interval
	return min
}

// GetNo 获取当前序号
func GetNo(id int64, tableName, fileName, condition string) int64 {
	var no int64
	sql := fmt.Sprintf("select %s from %s where where id = %d", fileName, tableName, id)
	gorm.GinDb.Raw(sql).Pluck(fileName, &no)
	return no
}

// Refresh 重置
func Refresh(id, beforeId int64, tableName, fieldName, condition string) int {
	var list []int64
	if condition != "" {
		condition = "And" + condition
	}
	gorm.GinDb.Table(tableName).Where("id != ?"+condition, id).Order(fieldName).Pluck("id", &list)
	baseIndex := 1
	for _, v := range list {
		gorm.GinDb.Table(tableName).Where("id = ?", v).UpdateColumn(fieldName, baseIndex*interval)
		baseIndex++
	}
	return baseIndex * interval
}

// TopByCondition 置顶
func TopByCondition(id int64, tableName, fieldName, condition string) {
	min := GenerateMinNo(tableName, fieldName, condition)
	if math.Abs(float64(min-interval)) > float64(maxOrder) {
		gorm.GinDb.Table(tableName).Where("id = ?", id).UpdateColumn(fieldName, 0)
		Refresh(id, 0, tableName, fieldName, condition)
	} else {
		err := gorm.GinDb.Table(tableName).Where("id = ?", id).UpdateColumn(fieldName, GenerateMinNo(tableName, fieldName, condition)).Error
		if err != nil {
			error2.CustomPanic("置顶失败")
		}
	}
}

// BottomByCondition 置底
func BottomByCondition(id int64, tableName, fieldName, condition string) {
	max := GenerateMaxNo(tableName, fieldName, condition)
	if math.Abs(float64(max+interval)) > float64(maxOrder) {
		gorm.GinDb.Table(tableName).Where("id = ?", id).UpdateColumn(fieldName, Refresh(id, 0, tableName, fieldName, condition))

	} else {
		err := gorm.GinDb.Table(tableName).Where("id = ?", id).UpdateColumn(fieldName, GenerateMaxNo(tableName, fieldName, condition)).Error
		if err != nil {
			error2.CustomPanic("置底失败")
		}
	}
}

// DragByCondition 推动
func DragByCondition(id, beforeId, afterId int64, tableName, fieldName, condition string) {
	if beforeId == 0 {
		//置顶
		TopByCondition(id, tableName, fieldName, condition)
	} else if afterId == 0 {
		//置底
		BottomByCondition(id, tableName, fieldName, condition)
	} else {
		//拖动
		b := GetNo(beforeId, tableName, fieldName, condition)
		a := GetNo(afterId, tableName, fieldName, condition)
		index := (b + a) / 2
		if index == b {
			//触发重新排序
			Refresh(id, beforeId, tableName, fieldName, condition)
		} else {
			//拖动
			err := gorm.GinDb.Table(tableName).Where("id = ?", id).UpdateColumn(fieldName, index).Error
			if err != nil {
				error2.CustomPanic("拖动失败")
			}
		}
	}
}
